package pl.mmadej.ds4java.ds.list;

import pl.mmadej.ds4java.ds.array.ArrayDecorator;
import pl.mmadej.ds4java.ds.common.BaseIterator;

import java.util.Comparator;
import java.util.Iterator;

public class ArrayList<T> extends AbstractList<T> implements List<T> {

    private ArrayDecorator<T> array;

    public ArrayList(int size) {
        array = new ArrayDecorator<T>(size);
    }

    @Override
    public boolean contains(T element) {
        return indexOf(element) != -1;
    }

    @Override
    public int indexOf(T element) {
        for (int i = 0; i < size(); i++) {
            if (array.elementAt(i).equals(element)) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public void insert(T element) {
        assertNotNull(element);
        array.setAt(size(), element);
        incrementSize();
    }

    @Override
    public void insertAt(int index, T element) {
        assertNotNull(element);
        assertIndexInBounds(index, 0, size());
        for (int i = size(); i > index; i--) {
            array.setAt(i, array.elementAt(i - 1));
        }
        array.setAt(index, element);
        incrementSize();
    }

    @Override
    public T searchAt(int index) {
        assertIndexInBounds(index, 0, size() - 1);
        return array.elementAt(index);
    }

    @Override
    public T deleteAt(int index) {
        assertIndexInBounds(index, 0, size() - 1);
        T element = array.elementAt(index);
        for (int i = index; i < size() - 1; i++) {
            array.setAt(i, array.elementAt(i + 1));
        }
        decrementSize();
        array.setAt(size(), null);
        return element;
    }

    @Override
    public boolean delete(T element) {
        int index = indexOf(element);
        if (index != -1) {
            deleteAt(index);
            return true;
        }
        return false;
    }

    @Override
    public T deleteLast() {
        if (empty()) {
            return null;
        }
        return deleteAt(size() - 1);
    }

    @Override
    public T deleteFirst() {
        if (empty()) {
            return null;
        }
        return deleteAt(0);
    }

    @Override
    public T searchLast() {
        if (empty()) {
            return null;
        }
        return array.elementAt(size() - 1);
    }

    @Override
    public T searchFirst() {
        if (empty()) {
            return null;
        }
        return array.elementAt(0);
    }

    @Override
    public List<T> toUnmodifiable() {
        return new UnmodifableList<T>(this);
    }

    @Override
    public <E> E[] toArray(E[] e) {
        return array.toArray(e, 0, size());
    }

    @Override
    public void sort(Comparator<T> comparator) {
        array.quickSort(0, size() - 1, comparator);
    }

    @Override
    public void clear() {
        array.clear();
        setSize(0);
    }

    @Override
    public void reverse() {
        array.reverse(0, size());
    }

    @Override
    public Iterator<T> iterator() {
        return new ArrayListIterator<T>();
    }

    private class ArrayListIterator<T> extends BaseIterator<T> {

        private int currentElementIndex;

        @Override
        public boolean hasNext() {
            return currentElementIndex < size();
        }

        @Override
        public T doNext() {
            return (T) array.elementAt(currentElementIndex++);
        }

        @Override
        public void doRemove() {
            deleteAt(--currentElementIndex);
        }

        protected boolean walkingNotStarted() {
            return currentElementIndex == 0;
        }

        protected boolean walkingEnded() {
            return currentElementIndex == size();
        }
    }
}
